<template>
	<el-card shadow="never" class="card-box">
		<template #header v-if="title">
			<div class="card-box-header">
				<div class="header-left">
					<span>{{ title }}</span>
				</div>
				<div class="header-right">
					<slot name="header_right" v-bind="{ data }"></slot>
				</div>
			</div>
		</template>
		<div class="card-box-body" v-if="!dictLoading">
			<slot v-bind="{ data }">
				<el-row :gutter="24">
					<template v-for="(item, index) in metadataList" :key="index">
						<el-col v-if="item._isShow!(item, data, item.prop)" :span="item.span">
							<div class="spanItem">
								<div class="spanItem-title">
									{{ item.label }}
									<el-tooltip v-if="item.alert" effect="dark" :content="item.alert" placement="top">
										<el-icon><InfoFilled /></el-icon>
									</el-tooltip>
								</div>
								<div class="spanItem-value">
									<slot :name="`prop_${item.prop}`" v-bind="{ item, data }">
										<component :is="componentInfo" v-bind="{ item }"></component>
									</slot>
								</div>
							</div>
						</el-col>
						<div v-if="item.isNewLine" class="new-line"></div>
					</template>
				</el-row>
			</slot>
		</div>
	</el-card>
</template>

<script setup lang="tsx" name="CardDescriptions">
import { ref } from 'vue'
import { CardDescriptionsItemProps, CompType } from './interface/index'
import { InfoFilled } from '@element-plus/icons-vue'
import dict from '@/utils/dict/index'
const props = withDefaults(
	defineProps<{
		title?: string // 标题
		metadata: CardDescriptionsItemProps[] // 元数据
		data: Record<string, any> // 数据
	}>(),
	{
		data: () => ({})
	}
)
// 包装 isShow 方法，实现类似监听器，让逻辑只在数据改变触发
function wrapIsShowFun(isShowFun: Function) {
	let oldVal: any = null
	return function (item: CardDescriptionsItemProps, data: Record<string, any>, prop: string | number) {
		// 调用 isShow 方法
		const newVal = isShowFun({ item, data, prop })
		if (oldVal !== newVal) {
			oldVal = newVal
			/* 数据改变触发逻辑 */
			// if (newVal) {
			// 	// 显示
			// 	onItemShow(item, data, prop)
			// } else {
			// 	// 隐藏
			// 	onItemHide(item, data, prop)
			// }
		}
		return newVal
	}
}
// 字典加载
const dictLoading = ref(true)
// 如果当前 dict为后台数据 需要请求数据，则调用该请求接口，获取 dict数据
const apiArr: Promise<any>[] = []
// 处理 metadata
for (const item of props.metadata as Partial<CardDescriptionsItemProps>[]) {
	// isShow
	const isShow = item.isShow

	let _isShow
	if (isShow !== undefined) {
		if (typeof isShow === 'function') {
			const wrapFun = wrapIsShowFun(isShow)
			_isShow = (item: CardDescriptionsItemProps, data: Record<string, any>, prop: string | number) => wrapFun(item, data, prop)
		} else {
			_isShow = () => isShow
		}
	} else {
		_isShow = () => true
	}
	item._isShow = _isShow

	// 字典处理
	if (item.dict) {
		if (typeof item.dict === 'function') {
			apiArr.push(item.dict().then(data => (item._dictData = data)))
		} else if (typeof item.dict === 'string') {
			apiArr.push(dict.$cache.getDict(item.dict).then(data => (item._dictData = data)))
		} else {
			item._dictData = item.dict
		}
	}
}
const metadataList = ref(props.metadata)
Promise.all(apiArr)
	.then(() => {})
	.finally(() => {
		dictLoading.value = false
	})
/* 组件信息 */
const componentInfo = ({ item }: { item: CardDescriptionsItemProps }): any => {
	const { _dictData, compType, compProps, prop, compDefault } = item
	const data = props.data
	const value = data[prop] ?? compDefault
	/* 组件映射，此对象放里面方便后面根据 item 数据修改*/
	const compMap: Record<CompType, () => any> = {
		text() {
			if (_dictData) {
				return <dict-text value={value} options={_dictData} {...compProps}></dict-text>
			}
			return <span>{value}</span>
		},
		tag() {
			if (_dictData) {
				return <dict-tag value={value} options={_dictData} {...compProps}></dict-tag>
			}
			return <el-tag {...compProps}>{value}</el-tag>
		}
	}

	let result
	if (compType) {
		result = compMap[compType]()
	} else {
		// 默认
		result = compMap.text()
	}
	return <>{result}</>
}
</script>
<style lang="scss">
.card-box {
	.card-box-body {
		> .el-tabs {
			position: relative;
			top: calc(var(--el-card-padding) * -1);
			.el-tabs__header {
				.el-tabs__nav {
					padding: 3px 0;
				}
				&::after {
					position: absolute;
					bottom: 0;
					left: calc(var(--el-card-padding) * -1);
					z-index: var(--el-index-normal);
					width: calc(100% + var(--el-card-padding) * 2);
					height: 1px;
					content: '';
					background-color: var(--el-border-color-light);
				}
			}
			.el-tabs__item {
				height: 54px;
				font-weight: 600;
				line-height: 54px;
			}
			.el-tabs__nav-wrap::after {
				height: 1px;
			}
		}
	}
}
</style>
<style scoped lang="scss">
// 通用详情页信息项样式
.card-box {
	.card-box-header {
		display: flex;
		justify-content: space-between;
		font-weight: 600;
		line-height: 24px;
		text-align: left;
	}
	.card-box-body {
		.el-tabs {
			.el-tabs__header {
				margin: 0;
			}
			.el-tabs__item {
				height: 54px;
				font-weight: 600;
				line-height: 54px;
			}
			.el-tabs__nav-wrap::after {
				height: 0;
			}
		}
		.spanItem {
			display: flex;
			flex-direction: column;
			align-content: space-around;
			width: 100%;
			margin-bottom: 24px;
			word-break: break-all;
			.spanItem-title {
				font-size: 14px;
				font-weight: 700;
				line-height: 15px;
				text-indent: 20px;
				border-left: 3px solid $color-primary;
			}
			.spanItem-value {
				min-height: 20px;
				padding-left: 22px;
				margin-top: 12px;
				font-size: 14px;
			}
		}

		// 用来换行的 dom
		:deep(.new-line) {
			width: 100%;
		}
	}
}
</style>
